package com.chia.multienty.core.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chia.multienty.core.cache.redis.service.api.StringRedisService;
import com.chia.multienty.core.domain.condition.MasterOrUserCondition;
import com.chia.multienty.core.domain.condition.UserCondition;
import com.chia.multienty.core.domain.condition.UserOrOAuthCondition;
import com.chia.multienty.core.domain.dto.AppInstanceDTO;
import com.chia.multienty.core.domain.dto.AppVersionDTO;
import com.chia.multienty.core.domain.dto.TenantPayLogDTO;
import com.chia.multienty.core.domain.enums.HttpResultEnum;
import com.chia.multienty.core.domain.enums.StatusEnum;
import com.chia.multienty.core.exception.KutaRuntimeException;
import com.chia.multienty.core.mapper.AppInstanceMapper;
import com.chia.multienty.core.mybatis.MTLambdaWrapper;
import com.chia.multienty.core.mybatis.service.impl.KutaBaseServiceImpl;
import com.chia.multienty.core.parameter.base.AppVersionDetailGetParameter;
import com.chia.multienty.core.parameter.tenant.*;
import com.chia.multienty.core.pojo.*;
import com.chia.multienty.core.properties.yaml.YamlMultientyProperties;
import com.chia.multienty.core.service.AppInstanceService;
import com.chia.multienty.core.service.AppVersionService;
import com.chia.multienty.core.service.TenantPayLogService;
import com.chia.multienty.core.strategy.pay.PayFactory;
import com.chia.multienty.core.strategy.pay.PayService;
import com.chia.multienty.core.strategy.pay.domain.PayPurpose;
import com.chia.multienty.core.strategy.pay.domain.PaySource;
import com.chia.multienty.core.strategy.pay.domain.PayType;
import com.chia.multienty.core.strategy.pay.domain.bo.WxPayResultAttachBO;
import com.chia.multienty.core.strategy.pay.domain.bo.WxPayResultBO;
import com.chia.multienty.core.strategy.pay.domain.request.MTNativePrepayRequest;
import com.chia.multienty.core.tools.IdWorkerProvider;
import com.chia.multienty.core.tools.MultientyContext;
import com.chia.multienty.core.util.ListUtil;
import com.chia.multienty.core.util.QRCodeUtil;
import com.chia.multienty.core.util.TimeUtil;
import com.github.yulichang.toolkit.MPJWrappers;
import io.jsonwebtoken.lang.Strings;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 * 应用实例 服务实现类
 * </p>
 *
 * @author Multi Tenant Auto Generator
 * @since 2024-03-27
 */
@Service
@RequiredArgsConstructor
@Conditional(UserOrOAuthCondition.class)
public class AppInstanceServiceImpl extends KutaBaseServiceImpl<AppInstanceMapper, AppInstance> implements AppInstanceService {

    private final StringRedisService stringRedisService;
    private final AppVersionService appVersionService;
    private final TenantPayLogService tenantPayLogService;
    private final YamlMultientyProperties properties;

    @Value("${spring.profiles.active}")
    private String active;
    @Override
    public AppInstanceDTO getDetail(AppInstanceDetailGetParameter parameter) {
        return selectJoinOne(AppInstanceDTO.class,
                        MPJWrappers.<AppInstance>lambdaJoin().eq(AppInstance::getInstanceId, parameter.getInstanceId()));
    }

    @Override
    public List<AppInstanceDTO> getMyAppInsList(Long tenantId) {
        return selectJoinList(AppInstanceDTO.class,
                mtLambdaWrapper()
                        .eq(AppInstance::getTenantId, tenantId)
        );
    }

    @Override
    public AppInstanceDTO getMyCurrentAppIns() {
        return selectJoinOne(AppInstanceDTO.class,
                mtLambdaWrapper()
                        .eq(AppInstance::getTenantId, MultientyContext.getTenant().getTenantId())
                        .eq(AppInstance::getAppId, MultientyContext.getMerchantAppId())
        );
    }
    @Override
    public AppInstanceDTO getMyCurrentAppIns(Long tenantId) {
        return selectJoinOne(AppInstanceDTO.class,
                mtLambdaWrapper()
                        .eq(AppInstance::getTenantId, tenantId)
                        .eq(AppInstance::getAppId, MultientyContext.getMerchantAppId())
        );
    }

    @Override
    public void update(AppInstanceUpdateParameter parameter) {
        AppInstance appInstance = new AppInstance();
        BeanUtils.copyProperties(parameter, appInstance);
        updateByIdTE(appInstance);
        stringRedisService.delete(new Tenant().getCacheKey(MultientyContext.getTenant().getTenantId()));
    }

    @Override
    public void delete(AppInstanceDeleteParameter parameter) {
        removeByIdTE(parameter.getInstanceId());
        stringRedisService.delete(new Tenant().getCacheKey(MultientyContext.getTenant().getTenantId()));
    }

    @Override
    public IPage<AppInstanceDTO> getPage(AppInstancePageGetParameter parameter) {
        return selectJoinListPage(parameter.getPageObj(), AppInstanceDTO.class,
                new MTLambdaWrapper<AppInstance>()
                        .solveGenericParameters(parameter)
                        .selectAll(AppInstance.class)
                        .selectAs(App::getName, AppInstanceDTO::getAppName)
                        .selectAs(Tenant::getCompanyName, AppInstanceDTO::getCompanyName)
                        .selectAs(AppVersion::getName, AppInstanceDTO::getVersionName)
                        .leftJoin(App.class, App::getAppId, AppInstance::getAppId)
                        .leftJoin(Tenant.class, Tenant::getTenantId, AppInstance::getTenantId)
                        .leftJoin(AppVersion.class, AppVersion::getVersionId, AppInstance::getVersionId)
                        .eq(parameter.getTenantId()!=null, AppInstance::getTenantId, parameter.getTenantId())
                        .eq(Strings.hasText(parameter.getKeywords()), AppInstance::getAlias, parameter.getKeywords())
                        .eq(parameter.getAppId()!=null, AppInstance::getAppId, parameter.getAppId())
                        .in(!ListUtil.isEmpty(parameter.getInstanceIds()),
                                AppInstance::getInstanceId,
                                parameter.getInstanceIds())
        );
    }

    @Override
    public void save(AppInstanceSaveParameter parameter) {
        if(exists(mtLambdaWrapper()
                .eq(AppInstance::getAppId, parameter.getAppId())
                .eq(AppInstance::getTenantId, parameter.getTenantId()))) {
            throw new KutaRuntimeException(HttpResultEnum.DUPLICATE_APP_INSTANCE);
        }

        AppInstance appInstance = new AppInstance();
        BeanUtils.copyProperties(parameter, appInstance);
        appInstance.setInstanceId(IdWorkerProvider.next());
        saveTE(appInstance);
        parameter.setInstanceId(appInstance.getInstanceId());
        stringRedisService.delete(new Tenant().getCacheKey(parameter.getTenantId()));
    }


    @Override
    public void enable(AppInstanceEnableParameter parameter) {
        AppInstance appInstance = new AppInstance();
        BeanUtils.copyProperties(parameter, appInstance);
        appInstance.setStatus(StatusEnum.NORMAL.getCode());
        updateByIdTE(appInstance);

    }

    @Override
    public void disable(AppInstanceDisableParameter parameter) {
        AppInstance appInstance = new AppInstance();
        BeanUtils.copyProperties(parameter, appInstance);
        appInstance.setStatus(StatusEnum.DISABLED.getCode());
        updateByIdTE(appInstance);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String renew(AppInstanceBecomeParameter parameter) throws Exception {
        AppInstanceDTO instance = getMyCurrentAppIns();
        if(instance == null) {
            throw new KutaRuntimeException(HttpResultEnum.SYSTEM_DATA_EMPTY);
        }
        AppVersionDTO appVersion = appVersionService.getDetail(new AppVersionDetailGetParameter().setVersionId(parameter.getVersionId()));
        Long outTradeNo = IdWorkerProvider.next();
        TenantPayLog payLog = new TenantPayLog()
                .setLogId(outTradeNo)
                .setAmount(appVersion.getPrice())
                .setDescribe(String.format("租户%s续费支付金额%s", MultientyContext.getTenant().getTenantId(), appVersion.getPrice()))
                .setOuterTradeNo(outTradeNo.toString())
                .setStatus(StatusEnum.WAITING.getCode())
                .setProgramId(1L)
                .setPayeeRemark(parameter.getVersionId().toString())
                .setPayerRemark(parameter.getOpType().equals(1) ? "become" : "renew")
                .setTenantId(MultientyContext.getTenant().getTenantId())
                .setTradeId(instance.getInstanceId())
                ;
        tenantPayLogService.saveTE(payLog);
        PayService payService = PayFactory.getPayService(PayType.WECHAT_V3);
        String url = payService.nativePrepay(
                new MTNativePrepayRequest()
                        .setMoney(appVersion.getPrice())
                        .setPurpose(PayPurpose.PLATFORM_RENEW)
                        .setSource(PaySource.PC)
                        .setDescription(String.format("%s软件续费", TimeUtil.toMonth(LocalDateTime.now())))
                        .setTenantId(MultientyContext.getTenant().getTenantId())
                        .setTradeNo(outTradeNo.toString())
        );
        String pathPrefix = properties.getFile().getLocal().get("path-prefix");
        String uuid =  UUID.randomUUID().toString().replaceAll("-","");
        String finalPath = Paths.get(pathPrefix, "qrcode", uuid + ".jpg").toString();

        QRCodeUtil.encode(url, null, finalPath, false);
        String qrcodeUrl = String.format("%s/local/qrcode/%s",
                properties.getDomain(),
                uuid + ".jpg"
        );
        return qrcodeUrl;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean handleWxPayNotify(WxPayResultBO result, WxPayResultAttachBO attach) {
        if(result.getResultCode().equals(StatusEnum.SUCCESS.getCode())) {
            TenantPayLogDTO payLog = tenantPayLogService.getDetail(new TenantPayLogDetailGetParameter().setLogId(Long.parseLong(result.getOutTradeNo())));
            AppInstance appInstance = getById(payLog.getTradeId());
            appInstance.setVersionId(Long.parseLong(payLog.getPayeeRemark()));
            appInstance.setInTrial(false);
            if(appInstance.getDeadline()!=null && appInstance.getDeadline().isAfter(LocalDateTime.now())) {
                appInstance.setDeadline(appInstance.getDeadline().plusYears(1));
            } else {
                appInstance.setDeadline(LocalDateTime.now().plusYears(1));
            }
            appInstance.setState(StatusEnum.WORKING.getCode());
            updateById(appInstance);
            payLog.setStatus(StatusEnum.PAID.getCode());
            tenantPayLogService.updateById(payLog);
        }
        return true;
    }
}
